/**
 * 
 */
package remato.client.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PartInitException;

import remato.client.controller.exceptions.RematoClientException;
import remato.client.model.ClientModelItem;
import remato.client.model.NodeChild;
import remato.client.model.NodeParent;
import remato.client.model.facade.IModelPersistable;
import remato.client.view.editor.dummy.DummyItem;
import remato.client.view.facade.IExtendedEditorPart;
import remato.client.view.util.EditorInput;

import com.wgo.bpot.common.removeme.Persistent;

/**
 * @author petterei
 * @version $Id: PersistableEditorWindowManager.java,v 1.1 2006-02-06 09:29:08 petterei Exp $
 */
public class PersistableEditorWindowManager implements EditorWindowManager {

	/** 
	 * Used for concepts with db-id  (can contain both dummies and real concepts)
	 */
	private Map<UniqueClassIdentifier<Long>, IExtendedEditorPart> editors = new Hashtable<UniqueClassIdentifier<Long>, IExtendedEditorPart>();
	
	/**
	 * Used when for unsaved consepts editors (has no dbId)
	 */
	private Map<Persistent, IExtendedEditorPart> nonPersistedConceptItemsEditors = new HashMap<Persistent, IExtendedEditorPart>();
	
	/** 
	 * Used when eclipse restores editors from last time eclipse was used (no editors should really be placed here..)
	 */
	private Set<IExtendedEditorPart> nonConceptItemsEditors = new HashSet<IExtendedEditorPart>();

	
	public synchronized void startEditorPart(String identifier, ClientModelItem input) {
		Long dbId = input.getIdentifier();
		Persistent persistent = input.getDomainModelObject();
		Class persistentType = input.getDomainObjectType();
		if (null != dbId && editors.containsKey(new UniqueClassIdentifier<Long>(dbId, persistentType))) {
			IExtendedEditorPart part =  editors.get(new UniqueClassIdentifier<Long>(dbId, persistentType));
			if ( ! part.getSite().getPage().isPartVisible(part)) {
				part.getSite().getPage().bringToTop(part);
			}
		} else if (nonPersistedConceptItemsEditors.containsKey(persistent)) {
			IExtendedEditorPart part = nonPersistedConceptItemsEditors.get(persistent);
			if ( ! part.getSite().getPage().isPartVisible(part)) {
				part.getSite().getPage().bringToTop(part);
			}
		} else {
			Assert.warningIfNull(persistent,getClass().getSimpleName() + " - opening editor: Not valid input for editor. (concept-id == null)" );
			// to be removed if never true..
			Assert.warningIfFalse(!(new Long(0).equals(dbId)), getClass().getSimpleName() + " - opening editor: Not valid input for editor. ConceptID == 0 !!!");
			try {
				getWorkbench().getActiveWorkbenchWindow().getActivePage().openEditor(new EditorInput(input) , identifier);
			} catch (PartInitException e) {
				throw new RematoClientException("Editor: " + Registry.TABLE_EDITOR_ID + " cannot be opened.", e);
			}
		}
	}
	
	public synchronized void addExtendedEditor(IExtendedEditorPart editor){
		IModelPersistable item = editor.getModelInput();
		Long dbId = item.getIdentifier();
		Persistent persistent = item.getDomainModelObject();
		if (null != dbId /* && null != persistent */ ) {
			editors.put(new UniqueClassIdentifier<Long>(dbId, item.getDomainObjectType()), editor);
			nonPersistedConceptItemsEditors.remove(persistent);
		} else if (null != persistent) {
			nonPersistedConceptItemsEditors.put(persistent, editor);
		} else {
			nonConceptItemsEditors.add(editor);
			RequirementPlugin.log(new ModelStatus(IStatus.WARNING, "An editor with invalid contents were registered."));
		}
	}
	
	@SuppressWarnings("unchecked")
	public synchronized void removeExtendedEditor(IExtendedEditorPart editor){
		IModelPersistable item = editor.getModelInput();
		Long dbId = item.getIdentifier();
		Persistent persistent = item.getDomainModelObject();
		if (null != dbId) {
			editors.remove(new UniqueClassIdentifier(dbId, item.getDomainObjectType()));
			nonPersistedConceptItemsEditors.remove(persistent);
		} else if (null != persistent) {
			nonPersistedConceptItemsEditors.remove(persistent);
		} else {
			nonConceptItemsEditors.remove(editor);
		}
	}
	
	/**
	 * @see remato.client.controller.EditorWindowManager#initEditors()
	 */
	public 	synchronized void initEditors() {
		Collection<IExtendedEditorPart> parts = new ArrayList<IExtendedEditorPart>();
		parts.addAll(editors.values());
		if (0 < editors.size()) {
			RequirementPlugin.log(new ModelStatus(IStatus.INFO, getClass().getSimpleName()+" Initiating " + editors.size() + " editors with updated contents."));
			int closedEditors = 0;
			for (IExtendedEditorPart part : parts) {
				IModelPersistable oldItem = part.getModelInput();
				IModelPersistable item = null;
				if (oldItem != null) {
					item = RequirementPlugin.getInstance().getSession().getModelItem(oldItem.getIdentifier());
				}
				if (null != item) {
					try {
						part.init((IEditorSite)part.getSite(), new EditorInput(item));
						part.inputChanged();
					} catch (PartInitException e) {
						RequirementPlugin.logAndDisplay(new ModelStatus(IStatus.WARNING, "Could not set new input..", e));
						closeEditor(part, false);
					}
				} else {
					closedEditors++;
					closeEditor(part, false);
				}
			}
			if (0 < closedEditors) {
				RequirementPlugin.log(new ModelStatus(IStatus.INFO, "Closed " + closedEditors + " editors due to unavailable content.."));
			}
		}
		closeNonPersistendItemEditors();
		closeNonConceptItemEditors();
		validatePersistentEditorRegistries();
	}
	
	private void closeNonPersistendItemEditors(){
		Set<IExtendedEditorPart> toBeClosed = new HashSet<IExtendedEditorPart>(nonPersistedConceptItemsEditors.size()); 
		toBeClosed.addAll(nonPersistedConceptItemsEditors.values());
		for (IExtendedEditorPart part : toBeClosed) {
			RequirementPlugin.log(new ModelStatus(IStatus.WARNING, "This editor ("+part.getTitle() +" "+ part.getTitleToolTip() + ") shoud not be open."));
			closeEditor(part, false);
		}
	}

	private void closeNonConceptItemEditors(){
		Set<IExtendedEditorPart> toBeClosed = new HashSet<IExtendedEditorPart>(nonPersistedConceptItemsEditors.size()); 
		toBeClosed = new HashSet<IExtendedEditorPart>(nonConceptItemsEditors.size()); 
		toBeClosed.addAll(nonConceptItemsEditors);
		int closedEditors = 0;
		for (IExtendedEditorPart part : toBeClosed){
			closeEditor(part, false);
			closedEditors++;
		}
		if (0 < closedEditors) {
			RequirementPlugin.log(new ModelStatus(IStatus.WARNING, "Closed "+ closedEditors +" non-concepts-editors (which should not occure)."));
		}
	}

	public synchronized void closeEditor(IModelPersistable item) {
		Long dbId = item.getIdentifier();
		Persistent persistent = item.getDomainModelObject();
		if (null != dbId) {
			IExtendedEditorPart part = editors.get(new UniqueClassIdentifier<Long>(dbId, item.getDomainObjectType()));
			if (null == part){
				part = nonPersistedConceptItemsEditors.get(persistent);
			}
			closeEditor(part, false);
		} else if (null != persistent) {
			IExtendedEditorPart part = nonPersistedConceptItemsEditors.get(persistent);
			closeEditor(part, false);
		} else {
			RequirementPlugin.log(new ModelStatus(IStatus.WARNING, "Inconsistent editors..", null));
		}
	}
	
	@SuppressWarnings("unchecked")
	public void closeEditorHierarchy(IModelPersistable item){
		closeEditor(item);
		if (item instanceof NodeParent) { // TODO: use INodeParent?
			NodeParent parent = (NodeParent)item;
			if (parent.hasChildren()) {
				for (NodeChild child : new ArrayList<NodeChild>(parent.getChildren())){
					closeEditorHierarchy(child);
				}
			}
		}
	}
	
	private void closeEditor(IExtendedEditorPart part, boolean save){
		if (null != part ) {
			part.getSite().getWorkbenchWindow().getActivePage().closeEditor(part,save);
//			getWorkbench().getActiveWorkbenchWindow().getActivePage().closeEditor(part, save);			
		}
	}

	/**
	 * @see remato.client.controller.EditorWindowManager#flushNonPersistedItemsEditors()
	 */
	public synchronized void flushNonPersistedItemsEditors() {
		Set<IExtendedEditorPart> toBeFlushed = new HashSet<IExtendedEditorPart>(nonPersistedConceptItemsEditors.size()); 
		toBeFlushed.addAll(nonPersistedConceptItemsEditors.values());
		for (IExtendedEditorPart part : toBeFlushed) {
			IModelPersistable item = part.getModelInput();
			if (null != item) {
				Long id = item.getIdentifier();
				if (null != id) {
					editors.put(new UniqueClassIdentifier<Long>(id, item.getDomainObjectType()), part);
					nonPersistedConceptItemsEditors.remove(part.getModelInput().getDomainModelObject());
					return;
				}
			}
			RequirementPlugin.log(new ModelStatus(IStatus.WARNING, "Not valid input for editor: " + part + " [closing]"));
			closeEditor(part, false);
		}
		closeNonConceptItemEditors();
		validatePersistentEditorRegistries();
	}
	
	private void validatePersistentEditorRegistries() {
		StringBuffer errorMessage = new StringBuffer();
		for (IExtendedEditorPart editor : editors.values()) {
			IModelPersistable item = editor.getModelInput();
			if (null == item || ( null != item && null == item.getIdentifier() ) ) {
				errorMessage.append(editor + "has invalid input");
			}
		}
		validateEmptyNonPersistedOrNonConceptsEditors(errorMessage);
		if (0 < errorMessage.length()) {
			RequirementPlugin.log(new ModelStatus(IStatus.ERROR, "Persistent editor registry inconsistent: " + errorMessage.toString()));
		}
	}
	
	private void validateEmptyNonPersistedOrNonConceptsEditors(StringBuffer errorMessage) {
		if (0 < nonPersistedConceptItemsEditors.size()) {
			errorMessage.append("There are " + nonPersistedConceptItemsEditors.size() + " non-persistent-concept editors registered - should have been 0.");
		}
		if (0 < nonConceptItemsEditors.size()) {
			errorMessage.append("There are " + nonConceptItemsEditors.size() + " non-concept editors registered - should have been 0.");
		}
	}
	
	/**
	 * @see remato.client.controller.EditorWindowManager#resetEditors()
	 */
	public void resetEditors() {
		flushNonPersistedItemsEditors();
		if (! getWorkbench().isClosing()) {
			for (IExtendedEditorPart part : editors.values()) {
				if (part.getModelInput() instanceof ClientModelItem ) {
					ClientModelItem clientItem = (ClientModelItem)part.getModelInput();
					DummyItem item = new DummyItem(clientItem.getIdentifier(), clientItem.getDomainObjectType(), clientItem.getText(), clientItem.getToolTipText());
					try {
						part.init((IEditorSite)part.getSite(), new EditorInput(item));
						part.inputChanged();
					} catch (PartInitException e) {
						RequirementPlugin.log(new ModelStatus(IStatus.WARNING, "Could not set new input..", e));
						getWorkbench().getActiveWorkbenchWindow().getActivePage().closeEditor(part, true);
					}
				}
			}
		}
		validatePersistentEditorRegistries();
	}
	
	private IWorkbench getWorkbench() {
		return RequirementPlugin.getInstance().getWorkbench();
	}

	/**
	 * @see remato.client.controller.EditorWindowManager#closeEditors()
	 */
	public void closeEditors() {
		Set<IExtendedEditorPart> toBeClosed = new HashSet<IExtendedEditorPart>(editors.size() + nonPersistedConceptItemsEditors.size() + nonConceptItemsEditors.size()); 
		toBeClosed.addAll(editors.values());
		toBeClosed.addAll(nonPersistedConceptItemsEditors.values());
		toBeClosed.addAll(nonConceptItemsEditors);
		for (IExtendedEditorPart editor : toBeClosed) {
			closeEditor(editor, true);
		}
		StringBuffer errorMessage = new StringBuffer();
		if (0 < editors.size()) {
			errorMessage.append("There are " + editors.size() + " normal editors registered - should have been 0.");
		}
		validateEmptyNonPersistedOrNonConceptsEditors(errorMessage);
		if (0 < errorMessage.length()) {
			RequirementPlugin.log(new ModelStatus(IStatus.ERROR, "Persistent editor registry inconsistent: " + errorMessage.toString()));
		}
	}
	
	
}
